package com.bfxy.disruptor.common;

import java.util.concurrent.locks.LockSupport;

/**
 * Sleeping strategy that initially spins, then uses a Thread.yield(), and
 * eventually sleep (LockSupport.parkNanos(1)) for the minimum
 * number of nanos the OS and JVM will allow while the EventProcessors are waiting on a barrier.
 */
//无锁的方式
public final class SleepingWaitStrategy implements WaitStrategy
{
    private static final int DEFAULT_RETRIES = 200;

    private final int retries;//200

    public SleepingWaitStrategy()
    {
        this(DEFAULT_RETRIES);
    }

    public SleepingWaitStrategy(int retries)
    {
        this.retries = retries;
    }

    @Override
    public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)
        throws AlertException, InterruptedException
    {
        long availableSequence;
        int counter = retries;//每次调用这个方法，counter从200开始

        while ((availableSequence = dependentSequence.get()) < sequence)
        {
            counter = applyWaitMethod(barrier, counter);//空循环，一直等着
        }

        return availableSequence;
    }

    @Override
    public void signalAllWhenBlocking()
    {
    }

    private int applyWaitMethod(final SequenceBarrier barrier, int counter) throws AlertException
    {
        barrier.checkAlert();

        if (counter > 100)//大于100就一直减1
        {
            --counter;
        }
        else if (counter > 0)
        {
            --counter;
            //让出时间片一会，有可能下一个还是自己执行
            Thread.yield();
            //线程让步。就是说当一个线程使用了这个方法之后，它就会把自己CPU执行的时间让掉，
            //让自己或者其它的线程运行，注意是让自己或者其他线程运行，并不是单纯的让给其他线程。
            //它能让当前线程由“运行状态”进入到“就绪状态”，从而让其它具有相同优先级的等待线程获取执行权；但是，并不能保
            //证在当前线程调用yield()之后，其它具有相同优先级的线程就一定能获得执行权；也有可能是当前线程又进入到“运行状态”继续运行！
            /*但是线程是有优先级的，优先级越高的人，就一定能第一个上车吗？这是不一定的，优先级高的人仅仅只是第一个上车的概率大了一点而已，
            	最终第一个上车的，也有可能是优先级最低的人。并且所谓的优先级执行，是在大量执行次数中才能体现出来的。*/
        }
        else
        {
            LockSupport.parkNanos(1L);//挂起当前线程
        }
        return counter;
    }
}
